/***************************************************************
|	File:		Game.cpp
|	Author:		John Gawne
|	Course:		Structure of Game Design
|	Purpose:	Game class controls the SGD wrappers
|				& runs the game state machine
***************************************************************/

#include "Game.h"

#include "../SGD Wrappers/SGD_AudioManager.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "../SGD Wrappers/SGD_InputManager.h"
#include "../SGD Wrappers/SGD_String.h"

#include "IGameState.h"
#include "MainMenuState.h"
#include <ctime>
#include <cstdlib>
#include <cassert>
#include "GameplayState.h"
#include <fstream>
#include "Camera.h"


#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
using namespace std;
std::wstring s2ws(const std::string& s);

#include <ShlObj.h>

/**************************************************************/
// Singleton
//	- instantiate the static member
/*static*/ Game* Game::s_pInstance = nullptr;

// GetInstance
//	- allocate the ONE instance & return it
/*static*/ Game* Game::GetInstance( void )
{
	if( s_pInstance == nullptr )
		s_pInstance = new Game;

	return s_pInstance;
}

// DeleteInstance
//	- deallocate the ONE instance
/*static*/ void Game::DeleteInstance( void )
{
	delete s_pInstance;
	s_pInstance = nullptr;
}


/**************************************************************/
// Initialize
//	- initialize the SGD wrappers
//	- load resources / assets
//	- allocate & initialize the game entities
bool Game::Initialize( int width, int height )
{
	// Seed First!
	srand( (unsigned int)time( nullptr ) );
	rand();

	// Store the size parameters
	m_nScreenWidth	= width;
	m_nScreenHeight = height;

	// Store the SGD singletons
	m_pAudio	= SGD::AudioManager::GetInstance();
	m_pGraphics = SGD::GraphicsManager::GetInstance();
	m_pInput	= SGD::InputManager::GetInstance();
	
	// Initialize each singleton
	if( m_pAudio->Initialize() == false 
	   || m_pGraphics->Initialize( true ) == false 
	   || m_pInput->Initialize() == false )
	{
		return false;
	}

	m_pFont = new BitmapFont();
	m_pFont->Initialize();

	int m_nNumItems = 0;
	int m_nMusicVolume = 0;
	int m_SFXVolume = 0;

	//std::fstream file;
	//file.open("resource/menu/Options.mnu", std::ios_base::in);
	//
	//if (file.is_open())
	//{
	//	file >> m_nNumItems;
	//	file.ignore(INT_MAX, '\n');
	//	for (unsigned char i = 0; i < m_nNumItems; i++)
	//	{
	//		char szTemp[100];
	//		file.getline(&szTemp[0], 100, '\n');
	//	}
	//
	//	char szTemp[100];
	//	file.getline(&szTemp[0], 100, '\n');
	//	if (szTemp[0] == 'Y')
	//		m_bFullScreen = true;
	//	else
	//		m_bFullScreen = false;
	//
	//	file >> m_nMusicVolume;
	//	file >> m_SFXVolume;
	//}
	//file.close();

	// Initialize empty save file
	{
		save = new Save;
		save->SavedSlot = 0;
		for (unsigned char i = 0; i < 3; i++)
		{
			save->Upgrades[i] = 0;

			SGD::String* temp = new SGD::String;
			*temp = L"Slot ";
			*temp += i + 49;
			*temp += L" New File";
			save->Time[i] = temp;
		}
		save->FullScreen = false;
		save->MusicVolume = 100;
		save->SFXVolume = 100;
		save->ScoreSize = 0;
	}

	HRESULT hr;
	ostringstream stringstream;
	char path[MAX_PATH];
	LPWSTR wszPath = NULL;
	size_t   size;

	// Get the path to the app data folder
	hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, 0, &wszPath);

	// Convert from LPWSTR to char[]
	wcstombs_s(&size, path, MAX_PATH, wszPath, MAX_PATH);
	
	// Convert char types
	if (hr == S_OK)
		stringstream << path;
	string pathtowrite = stringstream.str();

	string compName = "Tesseract";
	string gameName = "TimeToRun";

	// Add the company and game information
	pathtowrite += "\\" + compName + "\\" + gameName + "\\";

	


	// Create our save file
	pathtowrite += "SaveSlots.sav";

	// Make sure to read folder first to see if there is a file already writen
	// only needed for the first time to make sure there is no access violation
	fstream file;

	file.open(pathtowrite.c_str(), std::ios_base::binary | std::ios_base::in);
	{
		file.seekg(0,file.end);
		std::streamoff length = file.tellg();
		file.seekg(0, file.beg);
		
		if (file.is_open() && length >0)
		{
			file.read((char*)&save->SavedSlot, sizeof(save->SavedSlot));
	
			for (unsigned char i = 0; i < 3; i++)
			{
				file.read((char*)&save->Upgrades[i], sizeof(unsigned char));
	
				unsigned int strlength;
				file.read((char*)&strlength, sizeof(strlength));
	
				std::string t;
				t.resize(strlength);
				save->Time[i]->resize(strlength);
				file.read((char*)&t[0], strlength);
	
				for (unsigned char j = 0; j < strlength; j++)
					save->Time[i]->at(j) = t[j];
			}
	
			file.read((char*)&save->FullScreen, sizeof(save->FullScreen));
			file.read((char*)&save->MusicVolume, sizeof(save->MusicVolume));
			file.read((char*)&save->SFXVolume, sizeof(save->SFXVolume));

			file.read((char*)&save->ScoreSize, sizeof(save->ScoreSize));

			for (int i = 0; i < save->ScoreSize; i++)
			{
				float temp;
				file.read((char*)&temp, sizeof(temp));
				save->Score.push_back(temp);
			}
		}
	}


	SGD::AudioManager::GetInstance()->SetMasterVolume(SGD::AudioGroup::Music, save->MusicVolume);
	SGD::AudioManager::GetInstance()->SetMasterVolume(SGD::AudioGroup::SoundEffects, save->SFXVolume);

	
	SGD::Size resize = { (float)GetScreenWidth(), (float)GetScreenHeight() };

	SGD::GraphicsManager::GetInstance()->Resize(resize, save->FullScreen);


	// Start the game in the Main Menu state
	ChangeState( MainMenuState::GetInstance() );

	
	// Store the current time (in milliseconds)
	m_ulGameTime	= GetTickCount();

	return true;	// success!
}


/**************************************************************/
// Main
//	- update the SGD wrappers
//	- update the game entities
//	- render the game entities
int Game::Main( void )
{

	if( m_pAudio->Update() == false 
	   || m_pGraphics->Update() == false 
	   || m_pInput->Update() == false )
	{
		return -10;		// abort!
	}


	// Current time
	unsigned long now = GetTickCount();
	float elapsedTime = (now - m_ulGameTime) / 1000.0f;
	m_ulGameTime = now;

	// Cap the elapsed time to 1/8th of a second
	if( elapsedTime > 0.125f )
		elapsedTime = 0.125f;
	
	if (SGD::InputManager::GetInstance()->IsKeyDown(SGD::Key::Alt) == true && SGD::InputManager::GetInstance()->IsKeyPressed(SGD::Key::Enter) == true)
	{
		m_pGraphics->Resize({(float)m_nScreenWidth, (float)m_nScreenHeight},  togglescreen);
		togglescreen = !togglescreen;
		return 0;
	}
	// Let the current state handle input
	if( m_pCurrState->Input() == false )
		return 1;	// exit success!


	// Update & render the current state
	m_pCurrState->Update( elapsedTime );
	m_pCurrState->Render();
	return 0;		// keep playing!

}


/**************************************************************/
// Terminate
//	- deallocate game entities
//	- unload resources / assets
//	- terminate the SGD wrappers
void Game::Terminate( void )
{
	// For Save file
	Game* game = Game::GetInstance();
	HRESULT hr;
	ostringstream stringstream;
	char path[MAX_PATH];
	LPWSTR wszPath = NULL;
	size_t   size;

	// Get the path to the app data folder
	hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, 0, &wszPath);

	// Convert from LPWSTR to char[]
	wcstombs_s(&size, path, MAX_PATH, wszPath, MAX_PATH);

	// Convert char types
	if (hr == S_OK)
		stringstream << path;
	string pathtowrite = stringstream.str();

	string compName = "Tesseract";
	string gameName = "TimeToRun";

	// Add the company and game information
	pathtowrite += "\\" + compName + "\\" + gameName + "\\";

	std::wstring tempwString = s2ws(pathtowrite);
	LPCWSTR result = tempwString.c_str();

	// Create our directory
	SHCreateDirectoryEx(NULL, result, 0);

	// Create our save file
	pathtowrite += "SaveSlots.sav";
	fstream file;

	//std::fstream file;
	file.open(pathtowrite, std::ios_base::binary | std::ios_base::out);
	{
		// write out which save slot we are on
		file.write((char*)&save->SavedSlot, sizeof(save->SavedSlot));

		// loop through all 3 save slots
		for (unsigned char i = 0; i < 3; i++)
		{

			file.write((char*)&save->Upgrades[i], sizeof(unsigned char));

			unsigned int strlength = save->Time[i]->size();
			file.write((char*)&strlength, sizeof(unsigned int));
			std::string t;

			for (unsigned char j = 0; j < strlength; j++)
			{
				t += (char)save->Time[i]->at(j);
			}

			file.write((char*)t.c_str(), strlength);
		}
		file.write((char*)&save->FullScreen, sizeof(save->FullScreen));
		file.write((char*)&save->MusicVolume, sizeof(save->MusicVolume));
		file.write((char*)&save->SFXVolume, sizeof(save->SFXVolume));

		save->ScoreSize = save->Score.size();
		file.write((char*)&save->ScoreSize, sizeof(save->ScoreSize));
		for each (float score in save->Score)
			file.write((char*)&score, sizeof(score));
	}
	file.close();

	m_pFont->Shutdown();
	delete m_pFont;

	// Exit the current state
	ChangeState( nullptr );
	delete save;


	// Terminate & deallocate the SGD wrappers
	m_pAudio->Terminate();
	m_pAudio = nullptr;
	SGD::AudioManager::DeleteInstance();
	
	m_pGraphics->Terminate();
	m_pGraphics = nullptr;
	SGD::GraphicsManager::DeleteInstance();

	m_pInput->Terminate();
	m_pInput = nullptr;
	SGD::InputManager::DeleteInstance();
}

/**************************************************************/
// ChangeState
//	- exit the current state to release resources
//	- enter the new state to allocate resources
//	- DANGER! Exiting the current state can CRASH the program!
//	  The state can ONLY be exited from the
//	  Input, Update, and Render methods!!!
void Game::ChangeState( IGameState* pNewState )
{
	// Exit the old state
	if( m_pCurrState != nullptr )
		m_pCurrState->Exit();

	// Store the new state
	m_pCurrState = pNewState;

	// Enter the new state
	if( m_pCurrState != nullptr )
		m_pCurrState->Enter();
}
